﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint.Administration.Health;
using SDS.SPHealthAnalyzerRules.Delegation.Helpers;

namespace SDS.SPHealthAnalyzerRules.Delegation.Rules
{
    /// <summary>
    /// Ensures that each Excel Services service application is configured for constrained delegation WITH protocol transition
    /// </summary>
    /// <remarks>
    /// http://technet.microsoft.com/en-us/library/gg502605.aspx
    /// "To allow excel services to delegate the clients identity Kerberos constrained delegation must be configured."
    /// 
    /// 
    /// may at some point be worth checking that the service app instances are in the same proxy group as a webapp using kerb... but that'll be another day.
    /// </remarks>
    public class ExcelSvcs_Using_ConstrainedDelegation : SPHealthAnalysisRule
    {
        private Guid CorrelationToken = Guid.NewGuid();
        List<string> Errors = new List<string>();
        List<string> Remedies = new List<string>();

        public override SPHealthCategory Category { get { return SPHealthCategory.Configuration; } }
        public override SPHealthCheckErrorLevel ErrorLevel { get { return SPHealthCheckErrorLevel.Error; } }

        public override SPHealthAnalysisRuleAutomaticExecutionParameters AutomaticExecutionParameters
        {
            get
            {
                return new SPHealthAnalysisRuleAutomaticExecutionParameters()
                {
                    Schedule = SPHealthCheckSchedule.Daily,
                    Scope = SPHealthCheckScope.All,
                    ServiceType = SPFarm.Local.Services.First(s => s.GetType().FullName == "Microsoft.Office.Excel.Server.MossHost.ExcelServerWebService").GetType(),     // run on any server running Excel Services
                    RepairAutomatically = false
                };
            }
        }

        public override SPHealthCheckStatus Check()
        {
            var outval = SPHealthCheckStatus.Failed;

            LoggingService.Write(LoggingService.Category_Excel, TraceSeverity.Verbose, "Executing Health Check", CorrelationToken);

            Microsoft.SharePoint.SPSecurity.RunWithElevatedPrivileges(delegate { outval = doCheck(); });

            LoggingService.Write(
                LoggingService.Category_Excel,
                TraceSeverity.Verbose,
                String.Format("Health Check completed with outcome {0}", outval.ToString()),
                CorrelationToken);

            return outval;
        } // void Check()

        public override string Summary { get { return "DELEGATION: service account used by Excel Services not configured for constrained delegation"; } }
        public override string Explanation
        {
            get
            {
                return String.Format(
                    @"{0}Excel Services delegation requires the service account to use constrained delegation." +
                    @"{0}For more information, see http://technet.microsoft.com/en-us/library/gg502605.aspx" +
                    @"{0}" +
                    @"{0}The following errors were found:" +
                    @"{0}{1}",

                    Environment.NewLine,
                    String.Join(Environment.NewLine, Errors.ToArray())
                    ).Trim();
            }
        }
        public override string Remedy
        {
            get
            {
                return String.Format(
                        @"{0}The following corrective actions are required:" +
                        @"{0}{1}",

                        Environment.NewLine,
                        string.Join(Environment.NewLine, Remedies.ToArray())
                        ).Trim();
            }
        }







        private SPHealthCheckStatus doCheck()
        {
            // load any instances of Excel Services
            var ExcelServiceInstances = SPServer.Local
                                                .ServiceInstances
                                                .Where(si => si.Service.GetType().Name == "ExcelServerWebService")
                                                .ToList();

            // if no Excel Service instances exist, automatic pass
            if (!ExcelServiceInstances.Any())
                return SPHealthCheckStatus.Passed;

            // if no Excel Service apps exist, automatic pass
            var ExcelServiceApplications = ExcelServiceInstances.SelectMany(si => si.Service.Applications).ToList();
            if (!ExcelServiceApplications.Any())
                return SPHealthCheckStatus.Passed;

            // check each service app
            ExcelServiceApplications.ForEach(a =>
                {
                    var AccountName = (a as SPIisWebServiceApplication).ApplicationPool.ProcessAccount.LookupName().Split('\\').Last();
                    var DelegationSettings = Helpers.AD_Delegation.GetUser(AccountName);

                    if (DelegationSettings.Permission == AD_Delegation.DelegationPermission.NoDelegation)
                    {
                        Errors.Add(String.Format(@"Excel Services using service account [{0}] is not configured for delegation", AccountName));
                        Remedies.Add(String.Format(@"Enable delegation by adding an SPN to the service account (SPN does not need to be valid)"));
                        Remedies.Add(String.Format(@"Configure account to use CONSTRAINED delegation WITH protocol transition ('Use any authentication protocol')"));
                    }
                    else if (DelegationSettings.Permission == AD_Delegation.DelegationPermission.Unconstrained)
                    {
                        Errors.Add(String.Format(@"Excel Services using service account [{0}] is not configured for constrained delegation", AccountName));
                        Remedies.Add(String.Format(@"Reconfigure delegation to use CONSTRAINED delegation WITH protocol transition ('Use any authentication protocol')"));
                    }
                    else if (DelegationSettings.Permission == AD_Delegation.DelegationPermission.Constrained_KerbOnly)
                    {
                        Errors.Add(String.Format(@"Excel Services using service account [{0}] is not configured for constrained delegation with protocol transition", AccountName));
                        Remedies.Add(String.Format(@"Reconfigure constrained delegation to use protocol transition ('Use any authentication protocol')"));
                    }
                });

            return Errors.Any() ? SPHealthCheckStatus.Failed : SPHealthCheckStatus.Passed;

        } // doCheck()


    } // class
} // namespace
